﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using DMS.MechanicalReport.Integration.CopyServ;
using DMS.MechanicalReport.Integration.ListsServ;

namespace DMS.MechanicalReport.Integration
{
    /// <summary>
    /// This is a helper class for Mechanical Report to update SharePoint documents and metadata
    /// Methods required:
    /// 1. UploadFile - upload PDF document to sharepoint and update metadata
    /// 
    /// </summary>
    public class SharePointHelper
    {
        private static string _mechanicalReportWeb = "MechanicalReport";

        private static string _reportLibraryName = "Mechanical Reports";

        private static NetworkCredential _credential;
        private static string _siteCollectionUrl;


        public SharePointHelper()
        {

            _siteCollectionUrl = ConfigurationManager.AppSettings["SharePointDMSSiteUrl"];

            if( _siteCollectionUrl.Length == 0)
            {
                throw new Exception("Web Application Url and Site Collection Url can't be empty.");
            }


            if(!_siteCollectionUrl.EndsWith("/"))
            {
                _siteCollectionUrl = _siteCollectionUrl + "/";
            }

            string username = ConfigurationManager.AppSettings["SPWebServiceUsername"];
            string password = ConfigurationManager.AppSettings["SPWebServicePassword"];
            string domain = ConfigurationManager.AppSettings["SPWebServiceDomain"];
            _credential = new NetworkCredential(username,password,domain);
        }


        /// <summary>
        /// upload the document from NAV and return the document ID 
        /// </summary>
        /// <param name="docType"></param>
        /// <param name="year"></param>
        /// <param name="filename">the PDF document file name, should always be the format {auctionid}-{itemid}.pdf</param>
        /// <param name="contents"></param>
        /// <returns></returns>
        public bool UploadFile(string goodId, byte[] contents, MechanicalReportMetaData metaData)
        {
            if(String.IsNullOrEmpty(metaData.ReportID))
            {
                return false;
            }

            string siteUrl = _siteCollectionUrl;
            string copyServiceUrl = string.Format("{0}_vti_bin/copy.asmx", _siteCollectionUrl);
            


            var copyService = new Copy { Url = copyServiceUrl };
            
            //the account must have full control permission to the destination list/document library
            copyService.Credentials = _credential;

            string folderName = GetFolderName(goodId);

            //the name of the document will be renamed to goodid.pdf
            string fileUrl = string.Format("{0}{1}/{2}/{3}/{4}/{5}.pdf", siteUrl, _mechanicalReportWeb,
                                           _reportLibraryName, folderName, goodId, "NEWREPORT");
           

            //check if the document folder exist, create if not

            string reportSiteUrl = siteUrl + _mechanicalReportWeb;
            CreateFolderIfNotExist(reportSiteUrl, _reportLibraryName, folderName);
            CreateFolderIfNotExist(reportSiteUrl, _reportLibraryName, folderName + "/" + goodId);

            string[] destinationUrls = {fileUrl};

            FieldInformation fFieldInfo = new FieldInformation()
                                              {
                                                  DisplayName = "Description",
                                                  Type = FieldType.Text,
                                                  Value = fileUrl
                                              };
            FieldInformation[] fieldInformations = { fFieldInfo };

            CopyResult[] results;
            uint copyResult = copyService.CopyIntoItems(fileUrl, destinationUrls, fieldInformations, contents,
                                                        out results);

            string fileName = Path.GetFileName(fileUrl);
            string queryXml = "<Where><And><Eq><FieldRef Name='FileLeafRef' /><Value Type='File'>" + fileName +
             "</Value></Eq><Eq><FieldRef Name='ContentType' /><Value Type='Computed'>MechanicalReportDocument</Value></Eq></And></Where>";

            string itemId = GetItemID(reportSiteUrl, _reportLibraryName, queryXml, folderName + @"\" + goodId);
            if (!string.IsNullOrEmpty(itemId))
            {
                bool updateSuccess = UpdateMechanicalReport(reportSiteUrl, itemId, folderName , goodId, metaData);
                return updateSuccess;
            }
            return false;
        }

        private bool UpdateMechanicalReport(string siteUrl, string fileId, string hashFolder, string goodId, MechanicalReportMetaData metaData)
        {
            var listService = new Lists();

            listService.Url = siteUrl + "/_vti_bin/lists.asmx";

            listService.Credentials = _credential;


            string strBatch = "<Method ID='1' Cmd='Update'><Field Name='ID'>{0}</Field><Field Name='Title'>{1}</Field><Field Name='ReportID'>{2}</Field><Field Name='ReportType'>{3}</Field><Field Name='ReportVersion'>{4}</Field><Field Name='ReportRego'>{5}</Field><Field Name='ReportVIN'>{6}</Field><Field Name='ReportBranch'>{7}</Field><Field Name='ReportBranchCode'>{8}</Field></Method>";

            string itemRootFolder = string.Format("{0}/{1}/{2}", siteUrl, hashFolder,goodId);

            XmlNode ndListView = listService.GetListAndView(_reportLibraryName, "");
            string strListId = ndListView.ChildNodes[0].Attributes["Name"].Value;
            string strViewId = ndListView.ChildNodes[1].Attributes["Name"].Value;

            var doc = new XmlDocument();
            XmlElement batch = doc.CreateElement("Batch");


            batch.SetAttribute("OnError", "Continue");
            batch.SetAttribute("ListVersion", "1");
            batch.SetAttribute("ViewName", strViewId);
            batch.SetAttribute("RootFolder", itemRootFolder);

            batch.InnerXml = String.Format(strBatch, fileId, goodId , metaData.ReportID, metaData.ReportType,
                                             metaData.ReportVersion, metaData.Plate, metaData.Vin, metaData.BranchName, metaData.BranchCode);


            XmlNode node = listService.UpdateListItems(strListId, batch);
            if (node != null)
            {
                if (node.InnerText.Equals("0x00000000"))
                    return true;
            }
            return false;
        }


        private string GetFolderName(string filename)
        {
            int suffixdot = filename.IndexOf(".", System.StringComparison.Ordinal);

            if (suffixdot != -1)
            {
                filename = filename.Substring(0, suffixdot);
            }

            string md5 = CalculateMD5Hash(filename);
            return md5.Substring(0, 2);
        }

        public string CalculateMD5Hash(string input)
        {
            // step 1, calculate MD5 hash from input
            MD5 md5 = System.Security.Cryptography.MD5.Create();
            byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
            byte[] hash = md5.ComputeHash(inputBytes);

            // step 2, convert byte array to hex string
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hash.Length; i++)
            {
                sb.Append(hash[i].ToString("X2"));
            }
            return sb.ToString();
        }

        private void CreateFolderIfNotExist(string siteUrl, string docLibraryName, string folderName)
        {
            var listService = new Lists();
            if (!siteUrl.EndsWith("/"))
                siteUrl = siteUrl + "/";

            listService.Url = siteUrl + "_vti_bin/lists.asmx";
            listService.Credentials = _credential;
            //XmlNode list = listService.GetList(docLibraryName);

            string query = "<mylistitemrequest><Query><Where><And><Eq><FieldRef Name=\"FSObjType\" /><Value Type=\"Lookup\">1</Value></Eq><Eq><FieldRef Name=\"FileLeafRef\" /><Value Type=\"Text\">{0}</Value></Eq></And></Where></Query><ViewFields><FieldRef Name=\"FileRef\" /><FieldRef Name=\"Title\" /></ViewFields><QueryOptions><ViewAttributes Scope=\"RecursiveAll\" /></QueryOptions></mylistitemrequest>";


            XmlDocument doc = new XmlDocument();
            doc.LoadXml(String.Format(query, folderName));

            XmlNode queryNode = doc.SelectSingleNode("//Query");
            XmlNode viewNode = doc.SelectSingleNode("//ViewFields");
            XmlNode optionNode = doc.SelectSingleNode("//QueryOptions");

            XmlNode nodes = listService.GetListItems(docLibraryName, string.Empty, queryNode, viewNode, string.Empty,
                                                    optionNode, null);

            foreach (XmlNode node in nodes)
            {
                if (node.Name == "rs:data")
                {
                    if (node.Attributes != null)
                    {
                        int count = Convert.ToInt32(node.Attributes["ItemCount"].Value);
                        if (count == 0)
                        {
                            //create folder
                            string xmlconst = "<Batch OnError='Continue' ListVersion='1'><Method ID='1' Cmd='New'><Field Name='ID'>New</Field><Field Name='FSObjType'>1</Field><Field Name='BaseName'>{1}</Field></Method></Batch>";

                            doc.LoadXml(string.Format(xmlconst, siteUrl + docLibraryName, folderName));

                            XmlNode batchNode = doc.SelectSingleNode("//Batch");
                            XmlNode resultNodes = listService.UpdateListItems(docLibraryName, batchNode);
                        }
                    }
                }
            }
        }



        /// <summary>
        /// This function return item id based on query
        /// </summary>
        /// <param name="siteUrl"> </param>
        /// <param name="strListName">list name</param>
        /// <param name="queryXml"> </param>
        /// <param name="destinationFolderPath"> </param>
        /// <returns>look id</returns>
        private string GetItemID(string siteUrl, string strListName, string queryXml, string destinationFolderPath)
        {
            if (!siteUrl.EndsWith("/"))
                siteUrl = siteUrl + "/";
            var listReference = new Lists();
            string lookupId = string.Empty;
            try
            {
                listReference.Credentials = _credential;
                listReference.Url = siteUrl + "_vti_bin/lists.asmx";
                var xmlDoc = new XmlDocument();
                var query = xmlDoc.CreateElement("Query");
                var viewFields = xmlDoc.CreateElement("ViewFields");
                XmlElement queryOptions = xmlDoc.CreateElement("QueryOptions");
                query.InnerXml = queryXml;
                viewFields.InnerXml = "<FieldRef Name=\"ID\" />";
                if (!destinationFolderPath.Contains("\\"))
                    queryOptions.InnerXml = "<Folder>" + destinationFolderPath + "</Folder>";
                else
                {
                    queryOptions.InnerXml = "<Folder>" + strListName + @"\" + destinationFolderPath + "</Folder>";
                }
                XmlNode items = listReference.GetListItems(strListName, string.Empty, query, viewFields, string.Empty,
                                                                   queryOptions, null);

                foreach (XmlNode node in items)
                {
                    if (node.Name == "rs:data")
                    {
                        for (int i = 0; i < node.ChildNodes.Count; i++)
                        {
                            if (node.ChildNodes[i].Name == "z:row")
                            {
                                var xmlAttributeCollection = node.ChildNodes[i].Attributes;
                                if (xmlAttributeCollection != null) lookupId = xmlAttributeCollection["ows_ID"].Value;
                                break;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Error on retrieving Item ID: " + ex.Message);
            }
            finally
            {
                if (listReference != null)
                    listReference.Dispose();
            }

            return lookupId;
        }


        /// <summary>
        /// Read file and return bytes
        /// </summary>
        /// <param name="path">file path</param>
        /// <returns>file bytes</returns>
        public static byte[] GetFileFromFileSystem(string path)
        {
            byte[] fileBytes = null;

            if (File.Exists(path))
            {
                //read the file.
                using (FileStream fs = File.Open(path, FileMode.Open))
                {
                    fileBytes = new byte[fs.Length];
                    fs.Position = 0;
                    fs.Read(fileBytes, 0, Convert.ToInt32(fs.Length));
                }
            }
            return fileBytes;
        }
    }
}
